﻿using GifAnimator.AnimatedImage;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using Windows.Graphics.Imaging;
using Windows.Media.Capture;
using Windows.Media.MediaProperties;
using Windows.Storage;
using Windows.Storage.Search;
using Windows.Storage.Streams;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media.Imaging;
using Windows.UI.Xaml.Navigation;
using System.Runtime.InteropServices.WindowsRuntime;

namespace GifAnimator
{
    /// <summary>
    /// Une page vide peut être utilisée seule ou constituer une page de destination au sein d'un frame.
    /// </summary>
    public sealed partial class MainPage : Page
    {
        #region Attributs
        private MediaCapture mediaCapture = null;
        private readonly String TEMP_PHOTO_FILE_NAME = "photoTmp.jpg";
        private readonly String TEMP_GIF_FILE_NAME = "tmp.gif";
        private AnimatedImage.AnimatedImage animatedFile = null;
        private List<BitmapFrame> frames = new List<BitmapFrame>();
        private string intputFilename = "dino.gif";
        private string outputFilename = "output.gif";
        private StorageFile inputFile;
        private StorageFile outputFile;
        #endregion

        public MainPage()
        {
            this.InitializeComponent();

            try
            {
                this.Initialize();
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// Méthode d'initialisation
        /// </summary>
        public async void Initialize()
        {
            try
            {
                animatedFile = new AnimatedImage.AnimatedImage();
                ListImage.ItemsSource = animatedFile.Frames;

                inputFile = await StorageFile.GetFileFromPathAsync(Path.Combine(Windows.ApplicationModel.Package.Current.InstalledLocation.Path, intputFilename));
                outputFile = await KnownFolders.PicturesLibrary.CreateFileAsync(outputFilename, CreationCollisionOption.ReplaceExisting);
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// Invoqué lorsque cette page est sur le point d'être affichée dans un frame.
        /// </summary>
        /// <param name="e">Données d'événement décrivant la manière dont l'utilisateur a accédé à cette page. La propriété Parameter
        /// est généralement utilisée pour configurer la page.</param>
        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
            InitializeMediaCapture();
        }

        /// <summary>
        /// Initialise la capture du flux vidéo issu de la webcam
        /// </summary>
        private async void InitializeMediaCapture()
        {
            try
            {
                mediaCapture = new MediaCapture();

                var settings = new MediaCaptureInitializationSettings();
                settings.StreamingCaptureMode = StreamingCaptureMode.Video;

                await mediaCapture.InitializeAsync(settings);
            }
            catch (Exception e)
            {
                Debug.WriteLine(e.Message);
            }
        }

        /// <summary>
        /// Démarre a capture du flux vidéo
        /// </summary>
        private async void StartPreview()
        {
            previewCanvas.Source = mediaCapture;
            await mediaCapture.StartPreviewAsync();
        }

        /// <summary>
        /// Arrête la capture du flux vidéo
        /// </summary>
        private async void StopPreview()
        {
            await mediaCapture.StopPreviewAsync();
        }

        #region Import
        /// <summary>
        /// Read GIF file with .Net methods
        /// </summary>
        /// <returns></returns>
        private async void ImportGif(StorageFile sourceFile)
        {
            try
            {
                EnableUi(false);

                var gifFile = new AnimatedImage.AnimatedImage();
                var requests = new List<string>();
                requests.Add("/grctlext/Delay");

                using (var inputStream = await sourceFile.OpenAsync(FileAccessMode.Read))
                {
                    var decoder = await BitmapDecoder.CreateAsync(BitmapDecoder.GifDecoderId, inputStream);

                    for (uint i = 0; i < decoder.FrameCount; i++)
                    {
                        var frame = await decoder.GetFrameAsync(i);
                        var pixels = await frame.GetPixelDataAsync();

                        var tmpFile = await KnownFolders.PicturesLibrary.CreateFileAsync(TEMP_GIF_FILE_NAME, CreationCollisionOption.GenerateUniqueName);
                        using (var outputStream = await tmpFile.OpenAsync(FileAccessMode.ReadWrite))
                        {
                            var encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.GifEncoderId, outputStream);
                            encoder.SetPixelData(
                                frame.BitmapPixelFormat,
                                frame.BitmapAlphaMode,
                                frame.PixelWidth, frame.PixelHeight,
                                frame.DpiX, frame.DpiY,
                                pixels.DetachPixelData());
                            await encoder.FlushAsync();
                        }

                        // Get duration
                        UInt16 duration = 10;
                        try
                        {
                            var retrievedProperties = await decoder.BitmapProperties.GetPropertiesAsync(requests);
                            if (retrievedProperties.ContainsKey("/grctlext/Delay"))
                                duration = (UInt16)retrievedProperties["/grctlext/Delay"].Value;
                        }
                        catch (Exception e)
                        {
                            var test = 1;
                            test++;
                        }

                        var imgFrame = new ImageFrame(tmpFile, duration);
                        gifFile.Frames.Add(imgFrame);
                    }
                }

                animatedFile = gifFile;
                ListImage.ItemsSource = animatedFile.Frames;
            }
            catch (Exception ex)
            {
            }
            finally
            {
                EnableUi(true);
            }
        }
        private async void ImportGifUnmanaged(StorageFile sourceFile)
        {
            try
            {
                EnableUi(false);

                using (var inputStream = await sourceFile.OpenAsync(FileAccessMode.Read))
                {
                    var decoder = await BitmapDecoder.CreateAsync(BitmapDecoder.GifDecoderId, inputStream);

                    for (uint i = 0; i < decoder.FrameCount; i++)
                    {
                        var frame = await decoder.GetFrameAsync(i);
                        //animatedFile.Frames.Add(frame);
                    }
                }
            }
            catch (Exception ex)
            {
            }
            finally
            {
                EnableUi(true);
            }
        }
        #endregion

        #region Export

        /// <summary>
        /// Export le fichier GIF avec BitmapEncoder
        /// </summary>
        /// <param name="animatedImage"></param>
        private async void ExportGif(AnimatedImage.AnimatedImage animatedImage, StorageFile destinationFile)
        {
            try
            {
                EnableUi(false);

                using (var outputStream = await destinationFile.OpenAsync(FileAccessMode.ReadWrite))
                {
                    var encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.GifEncoderId, outputStream);

                    for (var i = 0; i < animatedImage.Frames.Count; i++)
                    {
                        var frame = animatedImage.Frames[i];

                        using (var inputStream = await frame.File.OpenAsync(FileAccessMode.Read))
                        {
                            var decoder = await BitmapDecoder.CreateAsync(inputStream);
                            var pixels = await decoder.GetPixelDataAsync();

                            // Save pixels
                            encoder.SetPixelData(
                                decoder.BitmapPixelFormat,
                                decoder.BitmapAlphaMode,
                                decoder.PixelWidth, decoder.PixelHeight,
                                decoder.DpiX, decoder.DpiY,
                                pixels.DetachPixelData());

                            // Set Delay
                            var propertieDelay = new List<KeyValuePair<string, BitmapTypedValue>>();
                            propertieDelay.Add(new KeyValuePair<string, BitmapTypedValue>("/grctlext/Delay", new BitmapTypedValue(frame.Duration, Windows.Foundation.PropertyType.UInt16)));
                            await encoder.BitmapProperties.SetPropertiesAsync(propertieDelay);

                            // Go next frame
                            if (i < animatedImage.Frames.Count - 1)
                                await encoder.GoToNextFrameAsync();
                        }
                    }

                    await encoder.FlushAsync();
                }
            }
            catch (Exception ex)
            {
                var i = 0;
                i++;
            }
            finally
            {
                EnableUi(true);
            }
        }

        /// <summary>
        /// Export le fichier GIF avec BitmapEncoder et l'utilisation d'un flux
        /// </summary>
        /// <param name="animatedImage"></param>
        /// <param name="destinationFile"></param>
        private async void ExportGifStream(AnimatedImage.AnimatedImage animatedImage, StorageFile destinationFile)
        {
            try
            {
                EnableUi(false);

                // Création d'un flux sur le fichier de sortie
                using (var outputStream = await destinationFile.OpenAsync(FileAccessMode.ReadWrite))
                {
                    // Création d'un encoder pour écrire dans le flux
                    var encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.GifEncoderId, outputStream);


                    for (int i = 0; i < animatedImage.Frames.Count; i++)
                    {
                        var frame = animatedImage.Frames[i];
                        var pixels = frame.Image.PixelBuffer.ToArray();



                        //var decoder = await BitmapDecoder.CreateAsync()

                        //using(var decoder = new BitmapDecoder())
                        //{

                        //}

                        // Save pixels
                        encoder.SetPixelData(
                            BitmapPixelFormat.Unknown,
                            BitmapAlphaMode.Ignore,
                            (uint)frame.Image.PixelWidth, (uint)frame.Image.PixelHeight,
                            50, 50,
                            pixels);

                        // Set Delay
                        var propertieDelay = new List<KeyValuePair<string, BitmapTypedValue>>();
                        propertieDelay.Add(new KeyValuePair<string, BitmapTypedValue>("/grctlext/Delay", new BitmapTypedValue(frame.Duration, Windows.Foundation.PropertyType.UInt16)));
                        await encoder.BitmapProperties.SetPropertiesAsync(propertieDelay);

                        // Go next frame
                        if (i < animatedImage.Frames.Count - 1)
                            await encoder.GoToNextFrameAsync();
                    }


                    //for (int i = 0; i < animatedImage.Frames.Count; i++)
                    //{
                    //    var frame = animatedImage.Frames[i];
                    //    var image = frame.Image;

                    //using (var inputStream = image.PixelBuffer.AsStream().AsInputStream().AsStreamForRead())
                    //{
                    //    var decoder = await BitmapDecoder.CreateAsync(inputStream);
                    //    var pixels = await decoder.GetPixelDataAsync();

                    //    // Save pixels
                    //    encoder.SetPixelData(
                    //        decoder.BitmapPixelFormat,
                    //        decoder.BitmapAlphaMode,
                    //        decoder.PixelWidth, decoder.PixelHeight,
                    //        decoder.DpiX, decoder.DpiY,
                    //        pixels.DetachPixelData());

                    //    // Set Delay
                    //    var propertieDelay = new List<KeyValuePair<string, BitmapTypedValue>>();
                    //    propertieDelay.Add(new KeyValuePair<string, BitmapTypedValue>("/grctlext/Delay", new BitmapTypedValue(frame.Duration, Windows.Foundation.PropertyType.UInt16)));
                    //    await encoder.BitmapProperties.SetPropertiesAsync(propertieDelay);

                    //    // Go next frame
                    //    if (i < animatedImage.Frames.Count - 1)
                    //        await encoder.GoToNextFrameAsync();
                    //}
                    //}

                    await encoder.FlushAsync();
                }
            }
            catch (Exception ex)
            {
            }
            finally
            {
                EnableUi(true);
            }
        }

        /// <summary>
        /// Export le fichier GIF en bas niveau
        /// </summary>
        /// <param name="animatedImage"></param>
        /// <param name="destinationFile"></param>
        private async void ExportGifUnmanaged(AnimatedImage.AnimatedImage animatedImage, StorageFile destinationFile)
        {
            try
            {
                EnableUi(false);

                var gifStream = new MemoryStream();
                using (var encoder = new GifEncoder(gifStream))
                {
                    foreach (var frame in animatedImage.Frames)
                    {
                        encoder.AddFrame(frame.Image, 0, 0, TimeSpan.FromMilliseconds(0));
                    }
                }
                gifStream.Seek(0, SeekOrigin.Begin);
                //gifStream.Position = 0;

                var outputStream = await destinationFile.OpenAsync(FileAccessMode.ReadWrite);

                await gifStream.CopyToAsync(outputStream.AsStream());
            }
            catch (Exception ex)
            {
            }
            finally
            {
                EnableUi(true);
            }
        }

        #endregion

        /// <summary>
        /// Joue le fichier GIF
        /// </summary>
        private void PlayGif()
        {
            foreach (var imageFrame in animatedFile.Frames)
            {
                ImagePreview.Source = imageFrame.Image;
                new System.Threading.ManualResetEvent(false).WaitOne(imageFrame.Duration);
            }
        }

        /// <summary>
        /// Charge toutes les images présentes dans la bibliothèque
        /// </summary>
        private async void LoadImages()
        {
            var query = KnownFolders.PicturesLibrary.CreateFileQuery();
            var queryOptions = new QueryOptions(CommonFileQuery.DefaultQuery, new List<string>() { ".jpg", ".png", ".jpeg" });
            query.ApplyNewQueryOptions(queryOptions);

            foreach (var file in await query.GetFilesAsync())
            {
                using (var stream = await file.OpenAsync(FileAccessMode.Read))
                {
                    var wb = new WriteableBitmap(1, 1);
                    wb.SetSource(stream);
                    animatedFile.Frames.Add(new ImageFrame(wb));
                }
            }
        }

        #region UI Methods

        /// <summary>
        /// Active/Desactive les contrôles utilisateur
        /// </summary>
        /// <param name="enable"></param>
        private void EnableUi(bool enable)
        {
            btnGif.IsEnabled = enable;
            btnPhoto.IsEnabled = enable;
            btnRead.IsEnabled = enable;
            btnStart.IsEnabled = enable;
            btnStop.IsEnabled = enable;
            btnWrite.IsEnabled = enable;
            btnLoadImage.IsEnabled = enable;
        }

        /// <summary>
        /// Clic bouton Start
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ButtonStartPreview_Click(object sender, RoutedEventArgs e)
        {
            StartPreview();
        }

        /// <summary>
        /// Clic bouton Stop
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ButtonStopPreview_Click(object sender, RoutedEventArgs e)
        {
            StopPreview();
        }

        /// <summary>
        /// Clic bouton Photo
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private async void ButtonPhoto_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                EnableUi(false);

                // TODO : Voir propriétés d'Encoding à utiliser
                var imageProperties = ImageEncodingProperties.CreateJpeg();

                // Enregistrement direct (flux) - BEST PRACTICE
                // WARN : Remplace le code ci-dessous
                //using (var imageStream = new InMemoryRandomAccessStream())
                //{
                //    // Récup flux image depuis webcam
                //    await mediaCapture.CapturePhotoToStreamAsync(imageProperties, imageStream);

                //    // RAZ position flux
                //    imageStream.Seek(0);

                //    // On enregistre le flux dans une image
                //    // Pas besoin de connaitre les dimensions de l'image
                //    var image = new WriteableBitmap(1, 1);
                //    image.SetSource(imageStream);
                //    animatedFile.Frames.Add(new ImageFrame(image));
                //}
                // WARN : Remplace le code ci-dessous

                // Enregistrement par fichier
                // WARN : A remplacer par le code ci-dessus
                var tmpFile = await KnownFolders.PicturesLibrary.CreateFileAsync(TEMP_GIF_FILE_NAME, CreationCollisionOption.GenerateUniqueName);
                await mediaCapture.CapturePhotoToStorageFileAsync(imageProperties, tmpFile);
                animatedFile.Frames.Add(new ImageFrame(tmpFile));
                // WARN : A remplacer par le code ci-dessus

                imageLast.Source = animatedFile.Frames.Last().Image;

                nbPhoto.Text = string.Format("Nb photo : {0}", animatedFile.Frames.Count);

                PlayGif();
            }
            catch (Exception ex)
            {
            }
            finally
            {
                EnableUi(true);
            }
        }

        /// <summary>
        /// Clic bouton Généré GIF
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnGif_Click(object sender, RoutedEventArgs e)
        {
            ExportGif(animatedFile, outputFile);
            //ExportGifStream(animatedFile, outputFile);
        }

        /// <summary>
        /// Clic bouton Write
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnRead_Click(object sender, RoutedEventArgs e)
        {
            ImportGif(inputFile);
            //ImportGifUnmanaged(inputFile);
        }

        /// <summary>
        /// Clic bouton Write
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnWrite_Click(object sender, RoutedEventArgs e)
        {
            //ExportGif(animatedFile, outputFile);
            //ExportGifStream(animatedFile, outputFile);
            ExportGifUnmanaged(animatedFile, outputFile);
        }

        /// <summary>
        /// Clic bouton Charger images
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void BtnLoadImage_Click(object sender, RoutedEventArgs e)
        {
            LoadImages();
        }

        /// <summary>
        /// Changement valeur Slide
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Slider_ValueChanged(object sender, Windows.UI.Xaml.Controls.Primitives.RangeBaseValueChangedEventArgs e)
        {
            if (imageLast != null)
                imageLast.Opacity = e.NewValue / 100;
        }

        #endregion

    }
}